# encoding: utf-8

require 'mechanize'
class Utility::Weibo < Utility::BasicTweet
  include Oauth2::Weibo

  # # # # # # # # # # # # # # # #
  #
  # 从这里开始是类方法
  #
  # # # # # # # # # # # # # # # #



  # # # # # # # # # # # # # # # #
  #
  # 从这里开始是对外开放的接口
  #
  # # # # # # # # # # # # # # # #

  def error_rescue(result)
    return false unless result["error"]
    logger.info ">遇到错误: 请求#{result["api_name"]}时,#{result["error_code"]}--#{result["error"]}."
    case result["error_code"].to_i
    when 10009

    when 10013

    when 21327, 21317, 21316, 21315, 21332 # token 过期
      self.status[result["api_name"]] = false
      logger.info ">处理错误: 将此账号的#{result["api_name"]}标记为过期"
      self.save
      return ERROR_RESCUE[:another_retry]
    when 20003 #被封号
      self.status[result["api_name"]] = false
      self.status["login"] = false
      logger.info ">处理错误: 此账号#{self.username}已经被新浪封锁"
      self.save
      return ERROR_RESCUE[:another_retry]
    when 21321 # 测试未被授权
      self.status[result["api_name"]] = false
      logger.info ">处理错误: 此账号#{self.username}测试未被授权"
      self.save
      return ERROR_RESCUE[:another_retry]
    when 10022 # IP请求频次超过上限
      return ERROR_RESCUE[:im_sorry]
    when 10023 # 用户请求频次超过上限
      self.status[result["api_name"]] = false
      logger.info ">处理错误: 此账号#{self.username}请求频次超过上限"
      return ERROR_RESCUE[:another_retry]
      self.save
    when 10024 # 用户请求频次超过上限
      self.status[result["api_name"]] = false
      logger.info ">处理错误: 此账号#{self.username}请求频次超过上限"
      return ERROR_RESCUE[:another_retry]
      self.save
    else
      return ERROR_RESCUE[:im_sorry]
    end
  end

  def update_status

  end

    # def update_status(api_name, is_expire = false)
    #   api_name = api_name.to_s
    #   if Time.now - self.last_use >= 3600 # 一小时
    #     self.api_used[api_name] = 0
    #   else
    #     self.last_use = Time.now
    #     self.api_used[api_name] += 1
    #   end
    #   if self.api_used[api_name] > Oauth2::WEIBO_API_LIMITED[api_name]
    #     self.status[api_name] = false
    #   # elsif is_expire
    #   #   self.status[api_name] = ENUM_STATUS[:expired]
    #   end
    #   save
    # end

    # def auto_access
    #   login(rel = false) if @is_login
    #   login_page = @http_client.get("https://api.weibo.com/oauth2/authorize?client_id=#{Oauth2::WEIBO_CLIENT_ID}&response_type=code&redirect_uri=#{Oauth2::WEIBO_CALLBACK_URL}")
    #   # code = login_page.params[:code]
    #   data = {
    #     'client_id' => Oauth2::WEIBO_CLIENT_ID,
    #     'client_secret' => Oauth2::WEIBO_CLIENT_SECRET ,
    #     'grant_type' => 'authorization_code',
    #     'redirect_uri' => Oauth2::WEIBO_CALLBACK_URL,
    #     'code' => code}
    #   access_page = @http_client.post('https://api.weibo.com/oauth2/access_token', data)
    # end



  def login(rel = false)
    if load_cookies && !rel
      self.status["login"] = true
      save
      tmp_page = http_client.get('http://login.sina.com.cn/member/my.php?entry=sso')
      if tmp_page.links[1].to_s == "退出"
        logger.info ">登录成功: 通过cookies登录"
        return @is_login = true
      end
    end
    tmp_page = http_client.post("http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.7)", login_data)
    wlr = tmp_page.search('script')[0].children.to_s.match(/\"[\w\W]*\"/).to_s.gsub('"', '')
    after_login_page = http_client.get(wlr)
    # TODO 判断是否登陆成功
    # parse可能出现异常!!!
    result_json = JSON.try 'parse', (after_login_page.body.match(/\{[\w\W]*\}/).to_s)
    if result_json && result_json["result"]
      save_cookies
      logger.info ">登录成功: 通过账号密码登陆 #{self.username}"
      self.status["login"] = true
      save
      @is_login = true
    else
      result_json ||= {}
      @errno = result_json["errno"]
      logger.info ">登录失败: #{result_json["errno"]}--#{result_json["reason"]}"
      self.status["login"] = false
      save
      @is_login = false
    end
  end

  def cookies
    http_client.cookie_jar
  end

  def tweets
    @tweets
  end

  def is_login?
    login_count = 1
    login
    while !@is_login
      break if login_count > 3 || @is_login
      break if @errno == '4049'
      login true if login_count == 2
      logger.info ">重试登录: #{self.username}第#{login_count}次"
      login_count += 1
    end
    if @is_login
      return true
    else
      logger.info ">登录出错: 此账号将被冻结,切换账号后重试"
      self.status["login"] = false
      save
      return false
    end
  end

  def auto_access
    if (Time.now - access_time > (Oauth2::WEIBO_EXPIRE_TIME.days - 2000))
      logger.info ">重新授权: 授权即将过期,重新授权"
      if is_login?
        http_client.get("https://api.weibo.com/oauth2/authorize?client_id=#{Oauth2::WEIBO_CLIENT_ID}&response_type=code&redirect_uri=#{Oauth2::WEIBO_CALLBACK_URL}")
        return true
      else
        return false
      end
    end
  end

  def search(result, options)
    return result unless is_login?
    params = biuld_params(options)
    while_time = 0
    result.tweets = []
    result.has_result = true
    while result.tweets.length < options[:per_page] && !result.is_finish
      break if (while_time += 1) > 8
      result.is_finish = true if options[:page].to_i > 45
      have_a_rest
      options[:page] += 1
      logger.info ">开始搜索: 当前账号是#{self.username},当前的参数是page=#{options[:page]}&#{params}."
      http_client.get("http://s.weibo.com/weibo/#{options[:keyword]}?page=#{options[:page]}&#{params}") do |page|
        # 这里很脆弱 script 13
        begin
          joo = page.search("script")[15].child.to_s.match(/\{[\w\W]*\}/).to_s
          # too = page.search("script")[11].child.to_s.match(/\{[\w\W]*\}/).to_s
          logger.info ">搜索状态: 获取到原始数据,准备解析"
          joo = JSON.parse(joo)
          # too = JSON.parse(too)
          result.total_num = Nokogiri.HTML(joo["html"]).search('.search_title_a').text.match(/[\d?\,]+/).to_s.gsub(',','').to_i
          tweets = Nokogiri.HTML(joo["html"])
          logger.info ">搜索完成: 总共有#{result.total_num}条结果"
          if options[:page] * 20 >= result.total_num
            logger.info ">搜索完成: 所有搜索内容均被返回"
            result.is_finish = true
          end
          if result.total_num == 0
            result.is_finish = true
          end
        rescue Exception => e
          logger.info ">搜索出错: 解析出错!!!"
          result.error = ERROR_RESCUE[:another_retry]
          result.msg   = {:type    => :error,
                          :content => "解析出错, 可能是当前账号被封锁, 或者新浪修改了搜索页面!" + e.to_s}
          return result
        end

        tweets.search('.feed_list').each do |weibo|
          if options[:detail]
            result.tweets << weibo.attributes['mid'].to_s
          else
            w = {}
            begin
              w[:mid]               = weibo.attributes['mid'].to_s
              w[:text]              = weibo.search('dd>p>em').to_s.gsub(/<[^>]*>/, '')
              w[:profile_image_url] = weibo.search('.face>a>img').attr('src').value
              w[:screen_name]       = weibo.search('.face>a').attr('title').value

              w[:created_at]        = weibo.search('.content>p.info>a').attr('title').value
              w[:source]            = weibo.search('.info>a').text
              w[:source]           += weibo.search('.approve_co').attr('title').value if weibo.search('.approve_co').present?
              w[:uid]               = weibo.search('.face>a').attr('href').value.match(/\d{6,13}/).to_s
              tmp_pic               = weibo.search '.piclist>li>img'
              w[:thumbnail_pic]     = tmp_pic.attr('src').value unless tmp_pic.empty?
              w[:bmiddle_pic]       = w[:thumbnail_pic].gsub('thumbnail', 'bmiddle') if w[:thumbnail_pic]
              w[:original_pic]      = w[:thumbnail_pic].gsub('thumbnail', 'original') if w[:thumbnail_pic]

              if w[:uid].nil? || w[:uid].empty?
                w[:uid] = weibo.search('.face>a>img').attr('usercard').value.match(/\d{6,13}/).to_s
              end
              # w[:weibo_mid] = weibo.search('.content>p.info>span>a')[0].attributes["action-data"].value.match(/mid=\d*/).to_s.gsub('mid=','')
            rescue Exception => test
              logger.info ">解析出错: #{test}"
            end
            if filtered(w[:source], options[:filter_sources])
              result.add_weibo w
            end
          end
        end
      end
      result.increase_rpage(options[:page])
      logger.info ">搜索完成: 本次从渣浪第#{result.result_page}页获取到 #{result.tweets.count} 条"
    end
    return result.make_success
  end

  # # # # # # # # # # # # # # # #
  #
  # 从这里开始是私有的辅助方法
  #
  # # # # # # # # # # # # # # # #

  private

  def have_a_rest
    gap_time = Time.now.to_i - self.last_use
    _gt = 10
    if gap_time <= _gt
      logger.info ">规避封号: 为了防止封号, 等待中(#{[_gt - gap_time, _gt].min}s)..."
      sleep([_gt - gap_time, _gt].min)
    end
    self.update_attributes :last_use => Time.now.to_i
  end

  def filtered(source, filter_sources)
    fs_flag = true
    filter_sources.each do |fs|
      fs_flag = false if source.match(fs)
    end
    fs_flag
  end

  def biuld_params(options)
    options.each do |k, v|
      options.delete(k) if v.nil? || v.to_s.empty?
    end
    options_buffer = ""
    if options[:msgtype]
      options_buffer += "scope="
      case options[:msgtype]
      when 1
        options_buffer += "ori=custom::"
      end
    end
    if options[:endtime]
      options_buffer += "&timescope=custom:#{options[:starttime]}:#{options[:endtime]}"
    else
      options_buffer += "&timescope=custom:#{options[:starttime]}:#{options[:searchtime]}"
    end
    # if options[:searchtype]
    #   case options[:searchtype]
    #   when 0

    #   when 1

    #   when 8
    #     options_buffer += "timescope=custom:#{options[:starttime]}:#{options[:endtime]}"
    #   end
    # end
    unless options[:province].nil? || options[:province].empty?
      options_buffer += "&region=custom:#{options[:province]}:#{options[:city] || 1000}"
    end
    options_buffer
  end

  def encode_password
    login_data if @login_info.empty?
    # rsa2
    if RbConfig::CONFIG['host_os'] == "mingw32"
      pw = http_client.get('http://flowy.oopsdata.net/s/get_pass', {:pubkey => @login_info["pubkey"],:servertime => @login_info["servertime"], :nonce => @login_info["nonce"],:password => password})
      @encode_password = pw.body
    end
    @encode_password ||= `node #{Rails.root}/lib/weibo.js #{@login_info["pubkey"]} #{@login_info["servertime"]} #{@login_info["nonce"]} #{password}`
    # wsse
    # weibo_login unless @servertime
    # pwd1 = Digest::SHA1.hexdigest password
    # pwd2 = Digest::SHA1.hexdigest pwd1
    # Digest::SHA1.hexdigest "#{pwd2}+#{@servertime}#{@nonce}"
  end

  def encode_username
    Base64.strict_encode64(username.sub("@","%40"))
  end

  def login_data
    @login_info = {}
    http_client.get("http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&client=ssologin.js(v1.4.7)&_=#{Time.now.to_i.to_s}") do |page|
      wl = JSON.parse(page.content.match(/\{[\w\W]*\}/).to_s)
      @login_info["servertime"] = wl["servertime"]
      @login_info["nonce"] = wl["nonce"]
      @login_info["pubkey"] = wl["pubkey"]
      @login_info["rsakv"] = wl["rsakv"]
    end
    { 'entry'=>'weibo', 'gateway'=>'1',
      'from'=>'', 'savestate'=>'7', 'userticket'=>'1',
      'ssosimplelogin'=>'1', 'vsnf'=>'1', 'su'=>encode_username,
      'service'=>'miniblog', 'servertime'=>@login_info["servertime"], 'nonce'=>@login_info["nonce"],
      'pwencode'=>'rsa2', 'rsakv'=>@login_info["rsakv"] , 'sp'=>encode_password,
      'encoding'=>'UTF-8', 'prelt'=>'115',
      'returntype'=>'META',
      'url'=>"http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack"
    }
  end

  def str_62_to_10(str62)
    i10 = 0
    i = 1
    str62.each_char do |c|
      n = str62.length - i
      i += 1
      i10 += str62keys.index(c) * (62 ** n)
    end
    i10
  end

  def str_10_to_62(int10)
    s62 = ''
    r = 0
    while int10 != 0
      s62 = str62keys[int10 % 62] + s62
      int10 = int10 / 62
    end
    s62
  end

  def save_cookies
    File.open("public/weibo_cookies/#{encode_username}.cookie", "w") do |file|
      http_client.cookie_jar.dump_cookiestxt(file)
    end
  end

  def load_cookies
    return false unless File.exist?("public/weibo_cookies/#{encode_username}.cookie")
    File.open("public/weibo_cookies/#{encode_username}.cookie", "r") do |file|
      http_client.cookie_jar.load_cookiestxt(file)
    end
  end

  def str62keys
  [
    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
  ]
  end

end
